Metasploit Payload Size

Or: "How big can my stagers be, really?"

The idea here is to parse through the Metasploit Project's available exploits to determine what the distribution of payload sizes is.

This can help make decisions for stager size optimization - if I have a great idea for a stager (or other exploit payload), but can't make it any smaller than 1k, is it worth it? What if it's 2k? And so on.

As it turns out, payloads over 2kb work with less than 20% of available exploits, and payloads over 1kb only work with about 60% - if you can't make your stager under 2k, you shouldn't expect to be able to use it very often at all.


In [10]:
%matplotlib inline

import os
import re
import sys
import numpy as np
import matplotlib

matplotlib.use('TkAgg')

import matplotlib.pyplot as plt

In [2]:
# Set up a path to the Metasploit project's code.
basepath = os.path.join('/', 'home', 'dnelson', 'projects', 'msf-stats')
rootdir = os.path.join(basepath, 'metasploit-framework', 'modules', 'exploits')
rootdir


Out[2]:
'/home/dnelson/projects/msf-stats/metasploit-framework/modules/exploits'

In [3]:
# Iterate through every exploit module, searching for the amount of space that exploit provides to fit a payload in.
all_sizes = []
for folder, subs, files in os.walk(rootdir):
    for filename in files:
        with open(os.path.join(folder, filename), 'r') as sploit:
            #print("parsing " + filename + "...")
            text = sploit.read()
            # remove all whitespace
            text = ''.join(text.split())
            space = re.search("\'Space\'=>(\d+)\,", text)
            # Note that if no payload size limit is specified, we simply ignore that module
            if space:
                all_sizes.append(int(space.group(1)))

print("Modules processed: " + str(len(all_sizes)))


Modules processed: 950

In [4]:
sorted_sizes = np.sort(all_sizes)
cumulative = np.cumsum(sorted_sizes)

print(sorted_sizes)

# looks to me like we should exclude that last one, since it's waaaaaaay larger than the rest
sorted_sizes = sorted_sizes[:-1]


[     100      127      128      128      130      153      160      164
      175      200      200      200      200      200      210      212
      213      220      227      228      232      232      236      250
      250      250      250      250      250      253      255      255
      256      256      256      256      256      260      284      296
      300      300      300      300      300      300      310      336
      336      340      344      344      344      350      370      370
      373      375      380      384      390      392      400      400
      400      400      400      400      400      400      400      400
      400      400      400      400      400      400      400      400
      400      400      400      400      400      400      400      400
      400      400      400      410      417      424      424      434
      440      444      450      450      450      450      450      450
      460      466      472      475      476      480      480      490
      490      498      500      500      500      500      500      500
      500      500      500      500      500      500      500      500
      500      500      500      500      500      500      500      500
      500      500      500      500      500      500      500      500
      500      500      500      500      500      500      512      512
      512      512      512      512      512      512      512      512
      512      512      512      512      512      512      512      512
      512      512      512      512      512      512      512      512
      512      512      512      512      512      512      512      512
      512      512      512      512      512      512      512      512
      512      512      512      526      550      550      550      550
      550      550      550      550      550      550      550      550
      600      600      600      600      600      600      600      600
      600      600      600      600      600      600      600      600
      600      600      600      600      600      600      600      600
      600      600      600      600      614      632      636      640
      650      650      650      650      650      650      650      650
      650      650      660      674      698      700      700      700
      700      700      700      700      700      710      712      728
      730      750      750      750      750      750      750      750
      750      750      750      750      750      750      750      750
      750      750      750      750      750      750      750      780
      800      800      800      800      800      800      800      800
      800      800      800      800      800      800      800      800
      800      800      800      800      800      800      800      800
      800      800      800      800      800      800      800      800
      800      800      800      800      800      800      800      830
      850      850      850      850      864      870      880      890
      890      896      900      900      900      900      900      900
      906      920      934      936      936      936      948      948
      950      954      956      960      964      970      978      978
      979      987      990      991      994     1000     1000     1000
     1000     1000     1000     1000     1000     1000     1000     1000
     1000     1000     1000     1000     1000     1000     1000     1000
     1000     1000     1000     1000     1000     1000     1000     1000
     1000     1000     1000     1000     1000     1000     1000     1000
     1000     1000     1000     1000     1000     1000     1000     1000
     1000     1000     1000     1000     1000     1000     1000     1000
     1000     1000     1000     1000     1000     1000     1000     1000
     1000     1000     1000     1004     1012     1012     1014     1014
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1024     1024     1024
     1024     1024     1024     1024     1024     1026     1040     1046
     1073     1104     1200     1200     1321     1408     1456     1500
     1500     1500     1500     1675     1770     1787     1800     1800
     1800     1900     2000     2000     2000     2000     2000     2000
     2000     2000     2000     2000     2000     2000     2000     2000
     2000     2000     2000     2000     2000     2000     2000     2000
     2000     2000     2000     2020     2048     2048     2048     2048
     2048     2048     2048     2048     2048     2048     2048     2048
     2048     2048     2048     2048     2048     2048     2048     2048
     2048     2048     2048     2048     2048     2048     2048     2048
     2048     2048     2048     2048     2048     2048     2048     2048
     2048     2048     2048     2048     2048     2048     2048     2048
     2048     2048     2048     2048     2048     2048     2048     2048
     2048     2048     2048     2048     2048     2048     2048     2048
     2048     2048     2048     2048     2048     2048     2052     2060
     2339     2339     2454     2500     3000     3000     3000     3000
     3000     3000     3000     3000     3000     3060     3072     3072
     3500     3841     3925     4000     4000     4000     4000     4000
     4000     4000     4000     4000     4000     4000     4000     4000
     4000     4000     4000     4000     4000     4000     4000     4000
     4000     4000     4000     4000     4000     4000     4096     4096
     4096     4096     4096     4096     4096     4096     4096     4096
     4096     4096     4096     4096     4096     4096     4096     4096
     4096     4096     4096     4100     4104     4108     4150     4500
     4658     4660     4720     5000     5000     5000     5000     5000
     5100     6000     6000     6144     6144     6596     7000     7000
     7516     8000     8000     8000     8000     8000     8000     8000
     8000     8000     8000     8000     8138     8190     8190     8190
     8190     8190     8190     8190     8192     8192     8192     8192
     8192     9844    10000    10000    10000    10000    10000    10000
    10000    10000    10000    10000    10240    10359    12288    16384
    16384    20480    20480    20480    20480    20480    20480    20480
    20480    20480    20480    20480    20480    20480    20480    20480
    20480    20480    20480    20480    20480    21112    21244    31337
    31337    31337    32767    32768    32768    32768    33692    50000
    60000    65535    65535    99999   262144   262144   262144   262144
   262144   262144   262144   262144   262144 10000000]

In [20]:
# Plot our sorted sizes against a fraction, 0 to 1, of all exploits.
plt.figure(figsize = (10,5))
plt.plot(sorted_sizes, np.linspace(1,0,len(sorted_sizes)), linewidth=3, color='black')
plt.xlim((0,10000))

plt.xlabel("Payload size (bytes)", size=16)
plt.ylabel("Fraction of exploits which\n accept that payload size", size=16)

# Plot some vertical lines for emphasis; a red line at 512 bytes, green at 1024, and blue at 2048.
plt.axvline(512, color='red', linewidth=2)
plt.axvline(1024, color='green', linewidth=2)
plt.axvline(2048, color='blue', linewidth=2)
plt.show()


We'll start with displaying payload size against the fraction of exploits which will work (or not work) for that size. It looks like any payload over 2048 bytes will only work with about 20% of exploits - a little less, in fact! If any of your stagers are just barely above 1024 bytes, it's well worth the effort to trim those last few bytes. Almost a quarter of exploits available in Metasploit have a payload size cutoff at 1024 bytes.

This chart can also be read as a probability: if I want to send a 1000 byte payload, and I pick an exploit at random (or, I find a vulnerable host at random), I have about a 60% chance that the exploit I end up with will be able to accomodate that payload. If my payload is 500 bytes, that probability becomes more than 90%.


In [22]:
plt.figure(figsize = (10,5))
plt.hist(sorted_sizes, 500)

plt.xlim((0,35000))
plt.yscale('log')

plt.xlabel("Payload Size (bytes)", size=16)
plt.ylabel("Number of Exploits, Log Scale", size=16)
plt.show()


The humble histogram finishes out our exploration today - note that it's on a log scale. This is significantly less useful than the above charts, since payload size is a cumulative number (i.e. smaller payloads still work in exploits with more than enough space for them), but this view is interesting in that it shows us where there are large clusters of exploits accepting a certain payload size.

But, what about platforms? Are our results being influenced by the category of the exploit? (Windows and Linux vs. Android and iOS, etc.)


In [8]:
sploitsByCategory = {}
for folder, subs, files in os.walk(rootdir):
    for filename in files:
        with open(os.path.join(folder, filename), 'r') as sploit:
            #print("parsing " + filename + "...")
            text = sploit.read()
            # remove all whitespace
            text = ''.join(text.split())
            space = re.search("\'Space\'=>(\d+)\,", text)
            # Note that if no payload size limit is specified, we simply ignore that module
            if space:
                # get the first folder in the exploits directory by
                # 1. Stripping off the rootdir using [len(rootdir):]
                # 2. Splitting on the folder separator (not platform-independent)
                # 3. Taking the first one (the zeroth one is '' since there's always a leading /)
                sploitType = folder[len(rootdir):].split('/')[1]
                try:
                    sploitsByCategory[sploitType].append(int(space.group(1)))
                except KeyError:
                    sploitsByCategory[sploitType] = []
                    sploitsByCategory[sploitType].append(int(space.group(1)))

# Yeah, that's right, a nested list comprehension. To print.
[str(sploits) + ": " + ",".join([str(num) for num in sploitsByCategory[sploits]]) for sploits in sploitsByCategory]


Out[8]:
['aix: 4104',
 'freebsd: 175,1024,128,1024,1024',
 'netware: 400,2020',
 'osx: 8192,512,1000,3841,400,300,1024,1024,300,1024,1024',
 'hpux: 200',
 'unix: 512,1024,1024,1024,1024,65535,1024,1024,262144,262144,6144,1024,1024,4000,1024,4000,1024,4000,32768,32768,512,127,1024,1024,4000,4000,10000,512,4000,8190,200,4096,512,8000,1024,1024,512,1024,1024,262144,1024,8000,8000,8190,16384,1024,8190,1024,512,512,512,4000,512,1024,4000,1024,512,512,1024,16384,262144,1024,1024,8192,1024,200,2000,2000',
 'dialup: 3000',
 'multi: 10000000,390,500,1024,1024,1024,4096,512,300,1024,20480,2048,344,1024,60000,512,344,466,1024,10000,2048,512,21244,10000,475,10000,10000,31337,1024,8000,31337,6144,2000,8190,4000,262144,4000,2000,1024,8190,1024,4000,4000,1024,8190,10000,262144,4000,31337,8192,1024,4096,10000,4000,5000,262144,2000,262144,4000,2048,8000,20480,1024,1024,2048,20480,20480,20480,20480,512,20480,20480,20480,20480,20480,20480,20480,20480,1024,400,4000,20480,1024,20480,20480,232,232,2000,1675,256,1024,1024,220',
 'irix: 512',
 'solaris: 800,1024,2000,1024,2000,2000,8192,1024,1024',
 'apple_ios: 1800,1800',
 'bsdi: 1000',
 'windows: 1024,500,880,1024,400,500,400,700,500,400,614,800,850,450,450,500,228,600,500,750,1024,450,600,1024,1024,1024,750,1024,2048,1000,3072,1024,550,370,1104,2048,1000,512,3072,1024,1024,1024,2048,410,2048,500,2048,2048,2048,1000,296,250,5000,600,460,1024,350,284,400,210,600,1000,500,1012,800,600,600,1024,1024,1000,512,512,600,550,153,450,373,450,550,212,6000,550,340,550,1770,600,550,2000,450,336,8000,800,1000,650,2048,4108,1000,1024,1024,800,1024,400,5000,750,1024,4000,936,1500,1024,1000,1024,1024,1000,1024,8000,2339,7516,1900,1000,600,1024,1024,2000,1000,1000,2048,1024,1024,550,1024,512,1024,4000,500,6000,1024,698,900,800,1024,750,2000,2048,2454,800,2000,1000,1024,3000,900,1024,1024,4000,1024,2048,4100,512,1024,4000,1200,2000,750,1000,1787,1000,1000,1024,600,2000,4000,2000,1024,512,1024,2048,1024,6596,2048,21112,2000,4000,1024,9844,750,750,1024,1000,1024,2000,1024,5100,256,4720,2048,650,392,375,2339,728,700,3000,1000,1024,5000,800,864,500,1000,1024,1024,4000,3060,250,750,1000,1408,2048,600,1800,800,1000,800,1024,400,400,1024,500,500,4096,2048,4096,4500,2048,2048,4096,4096,4096,7000,4096,4096,4096,4096,2048,12288,4096,400,400,400,710,400,3000,400,1024,750,512,500,600,256,936,1024,4658,2000,380,512,700,512,660,600,500,400,2048,3925,500,800,750,1000,512,400,1012,700,10359,800,850,1000,1024,4000,336,160,400,650,4096,730,750,2048,417,750,2500,1024,1024,936,4096,1024,236,512,712,1024,550,850,400,250,3500,5000,344,512,512,512,512,1024,4000,600,400,850,2000,476,4096,370,1024,1024,1024,2000,1000,1000,4000,4660,1321,750,1024,650,250,2000,4150,1024,1024,8000,4000,500,33692,2048,830,500,650,512,900,650,500,1000,512,7000,1000,400,800,400,400,1024,700,750,2048,1024,750,500,2052,400,2048,750,650,636,10240,512,750,950,600,650,472,3000,600,498,750,987,650,600,2048,256,2048,650,253,1000,400,600,800,2048,512,900,1024,2048,600,1024,600,500,800,800,800,800,1000,550,164,550,500,400,550,600,2048,600,1026,2048,700,800,500,600,500,700,750,4096,2048,2048,600,1200,300,1024,600,1024,1024,1024,1024,1000,1024,1024,1024,512,800,1024,1024,1024,1024,2048,1040,1024,1024,890,2048,1000,2048,1024,1024,1024,1000,8000,1000,2048,1024,400,1024,1024,700,1024,800,1024,800,1024,1000,640,20480,1024,800,800,500,2048,2000,970,1024,800,1024,1024,800,2048,1024,800,1024,1024,1024,1024,1024,1024,800,1024,1456,1024,1000,1024,800,1024,1024,526,1024,2048,1024,1024,1000,512,1000,1024,750,1024,512,1024,1024,890,1024,1000,978,1024,2048,960,1024,2048,1024,1024,500,1024,1024,920,1024,1024,1024,750,1024,500,2048,1000,800,2048,2048,500,2048,2048,1024,1000,2000,1000,1014,1024,800,2048,1000,4096,2048,1014,800,2048,1000,2048,1024,1000,800,1024,1024,500,384,1024,20480,1024,991,1024,1024,2048,2048,1024,1024,1024,1024,1024,2048,1024,800,2048,1024,20480,1024,1024,1024,956,1024,800,1024,800,2048,1024,1024,2048,1024,1000,1024,906,1024,2048,2000,1024,4096,1000,1000,2048,2048,948,2048,870,934,978,948,1024,1024,1000,1024,213,512,674,300,500,490,310,1500,1024,1000,424,300,1024,434,1000,424,1000,1000,480,800,444,1000,1000,1000,512,400,480,1500,900,800,1000,800,3000,260,1000,490,250,440,600,1000,3000,550,1024,896,1024,632,1024,500,500,400,600,979,500,500,500,954,2048,750,994,32767,500,990,2048,8138',
 'linux: 964,1073,256,512,1046,100,8000,227,3000,128,1000,130,512,512,32768,10000,780,10000,1024,512,1024,1024,255,8190,1000,1024,3000,1024,1024,262144,4096,500,10000,1024,1024,99999,1024,1024,1024,1024,200,1024,50000,8192,1500,1024,1024,1024,8000,1004,200,65535,2060,250,255,4096,900,1024,600,1024']

In [9]:
sortedSploits = {}
platforms = ['linux', 'windows', 'unix', 'multi']
for platform in platforms:
    sortedSploits[platform] = np.sort(sploitsByCategory[platform])

colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k']
plt.figure(figsize = (10,5))
for i, platform in enumerate(platforms):
    plt.plot(sortedSploits[platform], np.linspace(1,0,len(sortedSploits[platform])), linewidth=3, color=colors[i])

plt.xlim((0,10000))

plt.xlabel("Payload size (bytes)", size=16)
plt.ylabel("Fraction of exploits which\n accept that payload size", size=16)

plt.legend([platform for platform in platforms])

plt.show()


Interesting! So, it appears that if you've got a large payload you want to use, your best bet is to find a multi exploit to use it with, followed by unix, then linux, then Windows. For whatever reason, Windows does a better job than the rest at preventing the injection of large payloads.

Take note - if you're designing a stager that'll end up on the larger side, making it Windows-specific is the worst decision you can make - if it's not under 1024 bytes, the best case scenario is that it's useful on about 20% of hosts. (although once you get it under 1024 bytes, the platforms are essentially all the same)